package dawnland03.data.map.model;

import java.util.*;

/**
 * User: Petru Obreja (obrejap@yahoo.com)
 * Date: Sep 15, 2009
 * Time: 7:15:59 PM
 */
public class Matrix {
    private final Integer rows;
    private final Integer columns;
    private final Map<Integer, MatrixCell> matrixCells;
    private final Map<Integer, Set<MatrixCell>> matrixCellsByRows;
    private final Map<Integer, Set<MatrixCell>> matrixCellsByColumns;
    private final Map<Integer, Map<Integer, MatrixCell>> matrixCellsByCoordinates; //first key: x, second key: y

    private final Map<Integer, EntityMatrixCellLayer> entityMatrixCellLayerMap;

    public Matrix(Integer rows, Integer columns) {
        this.rows = rows;
        this.columns = columns;
        matrixCells = new HashMap<Integer, MatrixCell>();
        matrixCellsByRows = new HashMap<Integer, Set<MatrixCell>>();
        matrixCellsByColumns = new HashMap<Integer, Set<MatrixCell>>();
        matrixCellsByCoordinates = new HashMap<Integer, Map<Integer, MatrixCell>>();
        entityMatrixCellLayerMap = new HashMap<Integer, EntityMatrixCellLayer>();
    }

    public Integer getRows() {
        return rows;
    }

    public Integer getColumns() {
        return columns;
    }

    public Map<Integer, MatrixCell> getMatrixCells() {
        return matrixCells;
    }

    public MatrixCell[] getMatrixCellsAsArray() {
        return matrixCells.values().toArray(new MatrixCell[0]);
    }

    public MatrixCell getMatrixCell(Integer matrixCellId) {
        return matrixCells.get(matrixCellId);
    }

    public MatrixCell getMatrixCell(Integer x, Integer y) {
        if (x < 0 || x >= rows) {
            return null;
        }
        if (y < 0 || y >= columns) {
            return null;
        }
        Map<Integer, MatrixCell> xColumn = matrixCellsByCoordinates.get(x);
        if (xColumn != null) { //column not created yet
            return xColumn.get(y);
        }
        return null; //never here;
    }

    public EntityMatrixCellLayer getEntityMatrixCellLayer(Integer matrixCellId) {
        return entityMatrixCellLayerMap.get(matrixCellId);
    }

    public Map<MatrixDirection, MatrixCell> getNeighbours(MatrixCell node) {
        Map<MatrixDirection, MatrixCell> neighbours = new HashMap<MatrixDirection, MatrixCell>();
        int oddRowXShift = 0;
        if (node.isEvenRow()) {
            oddRowXShift = 1;
        }
        MatrixCell nodeUpRight = getMatrixCell(node.getX() + oddRowXShift, node.getY() - 1);
        if (nodeUpRight != null) {
            neighbours.put(MatrixDirection.RIGHT_UP, nodeUpRight);
        }
        MatrixCell nodeRight = getMatrixCell(node.getX() + 1, node.getY());
        if (nodeRight != null) {
            neighbours.put(MatrixDirection.RIGHT, nodeRight);
        }
        MatrixCell nodeDownRight = getMatrixCell(node.getX() + oddRowXShift, node.getY() + 1);
        if (nodeDownRight != null) {
            neighbours.put(MatrixDirection.RIGHT_DOWN, nodeDownRight);
        }
        MatrixCell nodeDownLeft = getMatrixCell(node.getX() - 1 + oddRowXShift, node.getY() + 1);
        if (nodeDownLeft != null) {
            neighbours.put(MatrixDirection.LEFT_DOWN, nodeDownLeft);
        }
        MatrixCell nodeLeft = getMatrixCell(node.getX() - 1, node.getY());
        if (nodeLeft != null) {
            neighbours.put(MatrixDirection.LEFT, nodeLeft);
        }
        MatrixCell nodeUpLeft = getMatrixCell(node.getX() - 1 + oddRowXShift, node.getY() - 1);
        if (nodeUpLeft != null) {
            neighbours.put(MatrixDirection.LEFT_UP, nodeUpLeft);
        }
        return neighbours;
    }

    /*
     * return null if the two matrixCells are not neighbours
     */

    public MatrixDirection getMatrixDirectionBetweenTwoNeighbours(Integer firstMatrixCellId, Integer secondMatrixCellId) {
        MatrixCell firstMatrixCell = getMatrixCell(firstMatrixCellId), secondMatrixCell = getMatrixCell(secondMatrixCellId);
        return getMatrixDirectionBetweenTwoNeighbours(firstMatrixCell, secondMatrixCell);
    }

    /*
     * return null if the two matrixCells are not neighbours
     */

    public MatrixDirection getMatrixDirectionBetweenTwoNeighbours(MatrixCell firstMatrixCell, MatrixCell secondMatrixCell) {
        if ((firstMatrixCell == null) || (secondMatrixCell == null)) {
            return null;
        }
        int oddRowXShift = 0;
        if (firstMatrixCell.isEvenRow()) {
            oddRowXShift = 1;
        }
        if ((firstMatrixCell.getX() + oddRowXShift == secondMatrixCell.getX()) && (firstMatrixCell.getY() == secondMatrixCell.getY() + 1)) {
            return MatrixDirection.RIGHT_UP;
        } else if ((firstMatrixCell.getX() == secondMatrixCell.getX() - 1) && (firstMatrixCell.getY().intValue() == secondMatrixCell.getY())) {
            return MatrixDirection.RIGHT;
        } else if ((firstMatrixCell.getX() + oddRowXShift == secondMatrixCell.getX()) && (firstMatrixCell.getY() == secondMatrixCell.getY() - 1)) {
            return MatrixDirection.RIGHT_DOWN;
        } else if ((firstMatrixCell.getX() + oddRowXShift == secondMatrixCell.getX() + 1) && (firstMatrixCell.getY() == secondMatrixCell.getY() - 1)) {
            return MatrixDirection.LEFT_DOWN;
        } else if ((firstMatrixCell.getX() == secondMatrixCell.getX() + 1) && (firstMatrixCell.getY().intValue() == secondMatrixCell.getY())) {
            return MatrixDirection.LEFT;
        } else if ((firstMatrixCell.getX() + oddRowXShift == secondMatrixCell.getX() + 1) && (firstMatrixCell.getY() == secondMatrixCell.getY() + 1)) {
            return MatrixDirection.LEFT_UP;
        }
        return null;
    }

    public MatrixCell addMatrixCell(Integer id, Integer x, Integer y, Integer type, Integer roadLevel) {
        MatrixCell matrixCell = new MatrixCell(id, x, y, type, roadLevel);
        matrixCells.put(matrixCell.getId(), matrixCell);
        Set<MatrixCell> row = matrixCellsByRows.get(y);
        if (row == null) {
            row = new LinkedHashSet<MatrixCell>();
            matrixCellsByRows.put(y, row);
        }
        row.add(matrixCell);

        Set<MatrixCell> column = matrixCellsByColumns.get(x);
        if (column == null) {
            column = new LinkedHashSet<MatrixCell>();
            matrixCellsByColumns.put(x, column);
        }
        column.add(matrixCell);

        Map<Integer, MatrixCell> xColumn = matrixCellsByCoordinates.get(x);
        if (xColumn == null) {
            xColumn = new HashMap<Integer, MatrixCell>();
            matrixCellsByCoordinates.put(x, xColumn);
        }
        xColumn.put(y, matrixCell);

        //init the entityMatrixCellLayerMap
        entityMatrixCellLayerMap.put(matrixCell.getId(), new EntityMatrixCellLayer(matrixCell.getId()));

        return matrixCell;
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < rows; i++) {
            Set<MatrixCell> row = matrixCellsByRows.get(i);
            for (MatrixCell matrixCell : row) {
                stringBuilder.append(matrixCell.toBigString()).append(" ");
            }
            stringBuilder.append("\n");
        }
        return stringBuilder.toString();
    }
}
